package cn.lingyangwl.agile.pay;

import cn.lingyangwl.agile.common.feign.content.FeignRequestContentHolder;
import cn.lingyangwl.agile.pay.config.PayClientProperties;
import cn.lingyangwl.agile.pay.model.rqrs.pay.AbstractMchAppRQ;
import cn.lingyangwl.agile.pay.model.rqrs.pay.AbstractRQ;
import cn.lingyangwl.agile.pay.model.rqrs.pay.AbstractRS;
import cn.lingyangwl.agile.pay.model.rqrs.pay.payorder.*;
import cn.lingyangwl.agile.pay.model.rqrs.pay.refund.RefundOrderQO;
import cn.lingyangwl.agile.pay.model.rqrs.pay.refund.RefundOrderRQ;
import cn.lingyangwl.agile.pay.model.rqrs.pay.refund.RefundOrderRS;
import cn.lingyangwl.framework.core.response.Resp;
import cn.lingyangwl.framework.core.response.RetOps;
import cn.lingyangwl.framework.core.utils.IpUtils;
import cn.lingyangwl.framework.sign.SignPayload;
import cn.lingyangwl.framework.sign.SignUtils;
import cn.lingyangwl.framework.tool.core.exception.BaseException;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.function.Supplier;

/**
 * @author shenguangyang
 */
@Slf4j
@Component
public class PayApiImpl implements PayApi {
    @Resource
    private PayFeignClient payFeignClient;

    @Resource
    private PayClientProperties properties;

    @Override
    public Resp<UnifiedOrderRS> unifiedOrder(UnifiedOrderRQ rq) {
        fillData(rq);
        rq.setNotifyUrl(properties.getPayNotifyUrl());
        return signRequest(rq, () -> payFeignClient.unifiedOrder(rq));
    }

    @Override
    public Resp<UnifiedOrderRS> continuePay(ContinuePayRQ rq) {
        fillData(rq);
        return signRequest(rq, () -> payFeignClient.continuePay(rq));
    }

    @Override
    public Resp<RefundOrderRS> refundOrder(RefundOrderRQ rq) {
        fillData(rq);
        rq.setNotifyUrl(properties.getRefundNotifyUrl());
        return signRequest(rq, () -> payFeignClient.refundOrder(rq));
    }

    @Override
    public Resp<PayOrderRS> getOrder(PayOrderQO rq) {
        fillData(rq);
        return signRequest(rq, () -> payFeignClient.getOrder(rq));
    }

    @Override
    public Resp<TradeDataRS> getTradeData(TradeDataQO rq) {
        fillData(rq);
        return signRequest(rq, () -> payFeignClient.getTradeData(rq));
    }

    @Override
    public Resp<RefundOrderRS> getRefundOrder(RefundOrderQO rq) {
        fillData(rq);
        return signRequest(rq, () -> payFeignClient.getRefundOrder(rq));
    }

    @Override
    public Resp<ClosePayOrderRS> closePayOrder(ClosePayOrderRQ rq) {
        fillData(rq);
        return signRequest(rq, () -> payFeignClient.closePayOrder(rq));
    }

    private <RQ extends AbstractRQ, RS extends AbstractRS> Resp<RS> signRequest(RQ rq,
                                                                                Supplier<ResponseEntity<Resp<RS>>> function) {
        try {
            // 判断是否需要签名, 如果是走的网关, 则必须经过签名, 否则不需要
            boolean isSign = StringUtils.isNotEmpty(properties.getBaseUrl()) &&
                    properties.getBaseUrl().contains(PayClientProperties.GATEWAY_BASE_URL);

            String secretId = properties.getSecretId();
            String secretKey = properties.getSecretKey();

            if (!isSign) {
                return function.get().getBody();
            }

            SignPayload signPayload = SignUtils.genSign(properties.getSignType(), secretId, secretKey, rq);
            FeignRequestContentHolder.get().setAddHeaders(signPayload.toMapOfStringType());
            ResponseEntity<Resp<RS>> responseEntity = function.get();
            Resp<RS> rsResp = responseEntity.getBody();
            assert rsResp != null;
            if (RetOps.of(rsResp).isSuccess()) {
                HttpHeaders headers = responseEntity.getHeaders();
                SignPayload respSignPayload = SignPayload.buildByFunction(headers::getFirst);
                SignUtils.checkSign(respSignPayload, secretKey, rsResp.getData());
            }
            return rsResp;
        } catch (Exception e) {
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            log.error("error: ", e);
            throw new BizException("系统异常");
        }
    }

    private void fillData(AbstractMchAppRQ rq) {
        if (StringUtils.isEmpty(rq.getAppId())) {
            rq.setAppId(properties.getMchAppId());
        }
        if (StringUtils.isEmpty(rq.getMchNo())) {
            rq.setMchNo(properties.getMchNo());
        }
        rq.setClientIp(IpUtils.getHostIp());
        rq.setVersion(properties.getVersion());
    }
}
